/*
	animations played:
	idle, threaten, turnjump, attackrun
*/

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(AudioSource))]

public class EnemyMonster : MonoBehaviour
{
	public float attackTurnTime = 0.7f;
	public float rotateSpeed = 120.0f;
	public float attackDistance = 17.0f;
	public float extraRunTime = 2.0f;
	public int damage = 1;
	
	public float attackSpeed = 5.0f;
	public float attackRotateSpeed = 20.0f;
	
	public float idleTime = 1.6f;
	
	public Vector3 punchPosition = new Vector3(0.4f, 0.0f, 0.7f);
	public float punchRadius = 1.1f;
	
	//Sounds
	//Played during idle state
	public AudioClip idleSound;
	//Played during the seek and attack modes
	public AudioClip attackSound;
	
	private float attackAngle = 10.0f;
	private bool isAttacking = false;
	private float lastPunchTime = 0.0f;
	
	public Transform target;
	
	//Cache a reference to the controller
	private CharacterController characterController; 
	
	//Cache a link to LevelStatus state machine script
	public LevelStatus levelStateMachine;
	
	public IEnumerator Start()
	{
		levelStateMachine = GameObject.Find("/Level1").GetComponent<LevelStatus>();
		characterController = GetComponent<CharacterController>();
		
		if(!levelStateMachine)
		{
			Debug.Log("EnemyMonster: ERROR! NO LEVEL STATUS SCRIPT FOUND");
		}
		
		if(!target)
		{
			target = GameObject.FindWithTag("Player").transform;		
		}
		
		animation.wrapMode = WrapMode.Loop;
		
		//Setup animations
		animation.Play("Idle");
		animation["Combat"].wrapMode = WrapMode.Once;
		animation["turnjump"].wrapMode = WrapMode.Once;
		animation["GetHit"].wrapMode = WrapMode.Once;
		animation["GetHit"].layer = 1;
		
		//initialize audio clip
		audio.clip = idleSound;
		
		yield return new WaitForSeconds(Random.value);
		
		//Just attack for now
		while(true)
		{
			//Don't do anything when idle
			//Wait for player to be in range
			yield return StartCoroutine("Idle");
			
			//Prepare, turn to player and attack him
			yield return StartCoroutine("Attack");
		}
		
	}	
	
	
	public IEnumerator Idle()
	{
		//If idle sound is not initialized, set and play
		if(idleSound)
		{
			if(audio.clip != idleSound)
			{
				audio.Stop();
				audio.clip = idleSound;
				audio.loop = true;
				audio.Play();
			}
		}
		
		//Don't do anything while idle
		yield return new WaitForSeconds(idleTime);
		
		//If the player is far
		//Idle unless dying
		while(true)
		{
			characterController.SimpleMove(Vector3.zero);
			yield return new WaitForSeconds(0.2f);
			
			Vector3 offset = transform.position - target.position;
			
			//If player is in range, halt idle
			if(offset.magnitude < attackDistance)
			{
				yield return null;
			}
		}
	}
	
	public float RotateTowardsPosition(Vector3 targetPos, float rotateSpeed)
	{
		//Compute relative point and get the angle towards it
		Vector3 relative = transform.InverseTransformPoint(targetPos);
		float angle = Mathf.Atan2(relative.x, relative.z) * Mathf.Rad2Deg;
		
		//Clamp it with the max rotation speed
		float maxRotation = rotateSpeed * Time.deltaTime;
		float clampedAngle = Mathf.Clamp(angle, -maxRotation, maxRotation);
		
		//Rotate
		transform.Rotate(0f, clampedAngle, 0f);
		//Return the current angle
		return angle;
	}		
	
	public IEnumerator Attack()
	{
		isAttacking = true;
		if(attackSound)
		{
			if(audio.clip != attackSound)
			{
				audio.Stop();
				audio.clip = attackSound;
				audio.loop = true;
				audio.Play();
			}
		}
		
		//Queue attack/run animation 
		//Set blend to 0, it will be blended later
		animation.Play("Running");
		
		//Wait so player can prepare while turning
		//When near an angle of 0, begin to move
		float angle;
		angle = 180.0f;
		float time;
		time = 0.0f;
		Vector3 direction;
		while((angle > 5) || (time < attackTurnTime))
		{
			time += Time.deltaTime;
			angle = Mathf.Abs(RotateTowardsPosition(target.position, rotateSpeed));
			float move = Mathf.Clamp01((90 - angle) / 90);
			
			//depending on the angle, start moving
			animation["Running"].weight = animation["Running"].speed = move;
			direction = transform.TransformDirection(Vector3.forward * attackSpeed * move);
			characterController.SimpleMove(direction);
			
			yield return null;
		}
		
		//Run towards player
		float timer = 0.0f;
		bool lostSight = false;
		
		while(timer < extraRunTime)
		{
			angle = RotateTowardsPosition(target.position, attackRotateSpeed);
			
			//The angle of forward direction and the player position is larger than 50 degrees
			//Out of Enemy field of view
			if(Mathf.Abs(angle) > 40)
			{
				lostSight = true;
			}
			
			//If lost sight, keep running for some time then stop attacking
			if(lostSight)
			{
				timer += Time.deltaTime;
			}
			
			//Just move forward at constant speed
			direction = transform.TransformDirection(Vector3.forward * attackSpeed);
			characterController.SimpleMove(direction);
			
			//Keep searching if hitting target
			Vector3 pos = transform.TransformPoint(punchPosition);
			
			if(Time.time > lastPunchTime + 0.3f && (pos-target.position).magnitude < punchRadius)
			{
				//deal damage
				target.SendMessage("ApplyDamage", damage);
				//Knock player backwards/sideways
				Vector3 slamDirection = transform.InverseTransformDirection(target.position - transform.position);
				slamDirection.y = 0.0f;
				slamDirection.z = 1.0f;
				if(slamDirection.x >=0)
				{
					slamDirection.x =1.0f;
				}
				else
				{
					slamDirection.x = -1.0f;
				}
				
				target.SendMessage("Slam", transform.TransformDirection(slamDirection));
				lastPunchTime = Time.time;
			}
		
			//Not moving forward
			//Stop Attacking player
			if(characterController.velocity.magnitude < attackSpeed * 0.3f)
			{
				break;
			}
		
			yield return null;
		}
		
		isAttacking = false;
		//Go back to idle animation
		animation.CrossFade("Idle");
	}
	
	public void ApplyDamage()
	{
		animation.CrossFade("GetHit");
	}
	
	public void OnDrawGizmosSelected()
	{
		Gizmos.color = Color.yellow;
		Gizmos.DrawWireSphere(transform.TransformPoint(punchPosition),punchRadius);
		Gizmos.color = Color.red;
		Gizmos.DrawWireSphere(transform.position, attackDistance);
	}
}